//
//  ViewController.swift
//  MvvmMusicListDemo
//
//  Created by aron on 2018/12/13.
//  Copyright © 2018 aron. All rights reserved.
//

import UIKit
import RxSwift
import RxCocoa

class ViewController: UIViewController {
    
    lazy var label: UILabel = {
        var label = UILabel()
        label.text = "TestLabel"
        return label
    }()
    
    let disposeBag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.view.addSubview(self.label)
        self.label.frame = CGRect(x: 10, y: 10, width: 300, height: 40)
        
        // 测试使用AnyObserver
        //        testAnyObserver()
        
        // 测试使用Observable2
//        testObservable2()
        
        // AnyObserverSubscribe
//        testAnyObserverSubscribe()
        
        // ObservableBindToBinder
//        testObservableBindToBinder()
        
        // Buffer
//        testBuffer()
        
        // Window
//        testWindow()
        
        // GroupBy
//        testGroupBy()
        
        // Amb
//        testAmb()
        
        // takeWhile
//        testTakeWhile()
        
        // takeUntil
//        testTakeUntil()
        
        // gesture
        testGesture()
    }
    
    // MARK: - TestCase
    
    
    func testAnyObserverBindTo() {
        let observer: AnyObserver<String> = AnyObserver { (event) in
            switch event {
            case .next(let text) :
                self.label.text = text
            default:
                break
            }
        }
        
        // bind需要导入RxCocoa
        let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
        observable.map({ (count) -> String in
            return "testAnyObserver当前索引数\(count)"
        }).bind(to: observer).disposed(by: disposeBag)
    }
    
    func testAnyObserverSubscribe() {
        let observer: AnyObserver<String> = AnyObserver { (event) in
            switch event {
            case .next(let text) :
                self.label.text = text
            default:
                break
            }
        }
        
        // bind需要导入RxCocoa
        let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
        observable.map({ (count) -> String in
            return "testAnyObserver当前索引数\(count)"
        }).subscribe(observer).disposed(by: disposeBag)
    }
    
    func testObservable() -> Void {
        let observable = Observable.of("a", "b")
        observable.subscribe(onNext: { (element) in
            print(element)
        }, onError: { (error) in
            print(error)
        }, onCompleted: {
            print("Completed")
        }) {
            print("Disposed")
        }
    }
    
    func testObservable2() -> Void {
        // bind方法对应的原型是 bind(onNext: @escaping (Self.E) -> Void) -> Disposable
        let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
        observable.map { (count) -> String in
            return "testObservable2当前索引\(count)"
            }.bind { (text) in
                self.label.text = text
            }.disposed(by: disposeBag)
    }
    
    func testObservableBindToBinder() -> Void {
        // bind方法对应的原型是 bind(onNext: @escaping (Self.E) -> Void) -> Disposable
        let observable = Observable<Int>.interval(0.5, scheduler: MainScheduler.instance)
        observable.map({ (count) -> CGFloat in
            return CGFloat(count)
        }).bind(to: self.label.rx.fontSize).disposed(by: disposeBag)
    }
    
    
    // MARK: - 数据转换操作  buffer window map flatMap
    
    
    /// Buffer
    func testBuffer() -> Void {
        let subject = PublishSubject<String>()
        // timeSpan: 每次发出的间隔
        // count: 缓存的个数，缓存的个数达到该值会把内容一起发出
        subject.buffer(timeSpan: 1, count: 3, scheduler: MainScheduler.instance)
            .subscribe(onNext: {
                print("buffer = \($0)")
            })
            .disposed(by: disposeBag)
        subject.onNext("a")
        subject.onNext("b")
        subject.onNext("c")
        
        subject.onNext("1")
        subject.onNext("2")
        subject.onNext("3")
    }
    
    func testWindow() -> Void {
        let subject = PublishSubject<String>()
        // timeSpan: 每次发出的间隔
        // count: 缓存的个数，缓存的个数达到该值会把内容一起发出
        subject.window(timeSpan: 1, count: 3, scheduler: MainScheduler.instance)
            .subscribe(onNext: {
                print("window = \($0)")
            })
            .disposed(by: disposeBag)
        subject.onNext("a")
        subject.onNext("b")
        subject.onNext("c")
        
        subject.onNext("1")
        subject.onNext("2")
        subject.onNext("3")
    }
    
    /// Map
    func testMap() -> Void {
        Observable.of(1, 2, 3)
            .map { $0 * 10 }
            .subscribe(onNext: { print($0) })
            .disposed(by: disposeBag)
    }
    
    /// FlatMap
    // map 在做转换的时候容易出现“升维”的情况。即转变之后，从一个序列变成了一个序列的序列。
    // 而 flatMap 操作符会对源 Observable 的每一个元素应用一个转换方法，将他们转换成 Observables。 然后将这些 Observables 的元素合并之后再发送出来。即又将其 "拍扁"（降维）成一个 Observable 序列。
    // 这个操作符是非常有用的。比如当 Observable 的元素本生拥有其他的 Observable 时，我们可以将所有子 Observables 的元素发送出来。
    func testFlatMap() -> Void {
        let subject1 = BehaviorSubject(value: "A")
        let subject2 = BehaviorSubject(value: "1")
        
        let variable = Variable(subject1)
        
        variable.asObservable()
            .flatMap { $0 }
            .subscribe(onNext: { print($0) })
            .disposed(by: disposeBag)
        
        subject1.onNext("B")
        variable.value = subject2
        subject2.onNext("2")
        subject1.onNext("C")
    }
    
    func testGroupBy() -> Void {
        let observable = Observable.of(1, 2, 3, 4, 5, 6, 7)
        observable.groupBy { (num) -> String in
            num % 2 == 0 ? "偶数" : "奇数"
            }.subscribe {[unowned self] (event) in
                switch event {
                case .next(let group):
                    group.asObservable().subscribe({ (event) in
                        print("key：\(group.key)    event：\(event) element: \(String(describing: event.element))")
                    })
                        .disposed(by: self.disposeBag)
                default:
                    print("")
                }
        }.disposed(by: disposeBag)
    }
    
    
    // MARK: - 过滤 filter take  skip
    func testFilter() -> Void {
        Observable.of(2, 30, 22, 5, 60, 3, 40 ,9)
            .filter {
                $0 > 10
            }
            .subscribe(onNext: { print($0) })
            .disposed(by: disposeBag)
    }
    
    // 该操作符用于过滤掉连续重复的事件。
    func testDistinctUntilChanged() -> Void {
        Observable.of(1, 2, 3, 1, 1, 4)
            .distinctUntilChanged()
            .subscribe(onNext: { print($0) })
            .disposed(by: disposeBag)
    }
    
    // 该方法实现仅发送 Observable 序列中的前 n 个事件，在满足数量之后会自动 .completed。
    func testTake() -> Void {
        Observable.of(1, 2, 3, 4)
            .take(2)
            .subscribe(onNext: { print($0) })
            .disposed(by: disposeBag)
    }
    
    // 该方法用于跳过源 Observable 序列发出的前 n 个事件。
    func testSkip() -> Void {
        Observable.of(1, 2, 3, 4)
            .skip(2)
            .subscribe(onNext: { print($0) })
            .disposed(by: disposeBag)
    }
    
    // MARK: - 条件和布尔操作符：amb、takeWhile、skipWhile等
    
    
    /// Amb
    // 当传入多个 Observables 到 amb 操作符时，它将取第一个发出元素或产生事件的 Observable，然后只发出它的元素。并忽略掉其他的 Observables。
    func testAmb() -> Void {
        let subject1 = PublishSubject<Int>()
        let subject2 = PublishSubject<Int>()
        let subject3 = PublishSubject<Int>()
        
        subject1
            .amb(subject2)
            .amb(subject3)
            .subscribe(onNext: { print($0) })
            .disposed(by: disposeBag)
        
        subject2.onNext(1)
        subject1.onNext(20)
        subject2.onNext(2)
        subject1.onNext(40)
        subject3.onNext(0)
        subject2.onNext(3)
        subject1.onNext(60)
        subject3.onNext(0)
        subject3.onNext(0)
        
        // output:
        // 1
        // 2
        // 3
    }
    
    
    /// takeWhile
    func testTakeWhile() -> Void {
        Observable.of(2, 3, 4, 5, 6)
            .takeWhile { $0 < 4 }
            .subscribe(onNext: { print($0) })
            .disposed(by: disposeBag)
        
        // output:
        // 2
        // 3
    }
    
    /// takeUntil
    func testTakeUntil() -> Void {
        let source = PublishSubject<String>()
        let notifier = PublishSubject<String>()
        
        source
            .takeUntil(notifier)
            .subscribe(onNext: { print($0) })
            .disposed(by: disposeBag)
        
        source.onNext("a")
        source.onNext("b")
        source.onNext("c")
        source.onNext("d")
        
        //停止接收消息
        notifier.onNext("z")
        
        source.onNext("e")
        source.onNext("f")
        source.onNext("g")
        
        // output:
        // a
        // b
        // c
        // d
    }
    
    
    // MARK: - Cocoa Support
    
    
    /// gesture
    func testGesture() -> Void {
        let swipe = UISwipeGestureRecognizer()
        swipe.direction = .up
        self.view.addGestureRecognizer(swipe)
        
        swipe.rx.event.subscribe(onNext: { (gesture) in
            let position = gesture.location(in: self.view)
            print("subscribe向上滑动 \(position.x) \(position.y)")
        }).disposed(by: disposeBag)
        
        swipe.rx.event.bind { (gesture) in
            let position = gesture.location(in: self.view)
            print("bind向上滑动 \(position.x) \(position.y)")
        }.disposed(by: disposeBag)
    }
    
}


// MARK: - Extension

extension UILabel {
    public var fontSize: Binder<CGFloat> {
        return Binder(self) { label, fontSize in
            label.font = UIFont.systemFont(ofSize: fontSize)
        }
    }
}


// 对应UI进行扩展
extension UILabel {
    public var fontSize2: Binder<CGFloat> {
        get {
            return Binder(self, binding: { (label, fontSize) in
                label.font = UIFont.systemFont(ofSize: fontSize)
            })
        }
    }
}

// 对Reactive类进行扩展
extension Reactive where Base: UILabel {
    public var fontSize: Binder<CGFloat> {
        return Binder<CGFloat>(self.base, binding: { (label, fontSize) in
            label.font = UIFont.systemFont(ofSize: fontSize)
        })
    }
}

